﻿using Acme.BookStore2.Permissions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Threading;

namespace Acme.BookStore2
{
    [Authorize(BookStore2Permissions.OrganizationUnits.Default)]
    public class OrganizationUnitAppService : BookStore2AppService, IOrganizationUnitAppService
    {
        private readonly OrganizationUnitManager _organizationUnitManager;
        private readonly IOrganizationUnitRepository _organizationUnitRepository;
        private readonly IdentityUserManager _identityUserManager;

        public OrganizationUnitAppService(
            OrganizationUnitManager organizationUnitManager,
            IOrganizationUnitRepository organizationUnitRepository,
            IdentityUserManager identityUserManager)
        {
            _organizationUnitManager = organizationUnitManager;
            _organizationUnitRepository = organizationUnitRepository;
            _identityUserManager = identityUserManager;
        }

        public async Task<OrganizationUnitDto> GetAsync(Guid id)
        {
            return ObjectMapper.Map<OrganizationUnit, OrganizationUnitDto>(
                await _organizationUnitRepository.GetAsync(id)
            );
        }

        public async Task<ListResultDto<OrganizationUnitDto>> GetListAllAsync()
        {
            var list = await _organizationUnitRepository.GetListAsync();
            return new ListResultDto<OrganizationUnitDto>(
                ObjectMapper.Map<List<OrganizationUnit>, List<OrganizationUnitDto>>(list)
            );
        }

        public async Task<PagedResultDto<OrganizationUnitDto>> GetListAsync(PagedAndSortedResultRequestDto input)
        {
            var list = await _organizationUnitRepository.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount);
            var totalCount = await _organizationUnitRepository.GetCountAsync();

            return new PagedResultDto<OrganizationUnitDto>(
                totalCount,
                ObjectMapper.Map<List<OrganizationUnit>, List<OrganizationUnitDto>>(list)
                );
        }

        [Authorize(BookStore2Permissions.OrganizationUnits.ManageOU)]
        public async Task<OrganizationUnitDto> CreateAsync(OrganizationUnitCreateDto input)
        {
            var ou = new OrganizationUnit(
                GuidGenerator.Create(),
                input.DisplayName,
                parentId: input.ParentId,
                tenantId: CurrentTenant.Id
            );

            input.MapExtraPropertiesTo(ou);

            await _organizationUnitManager.CreateAsync(ou);
            await CurrentUnitOfWork.SaveChangesAsync();

            return ObjectMapper.Map<OrganizationUnit, OrganizationUnitDto>(ou);
        }

        [Authorize(BookStore2Permissions.OrganizationUnits.ManageOU)]
        public async Task<OrganizationUnitDto> UpdateAsync(Guid id, OrganizationUnitUpdateDto input)
        {
            var ou = await _organizationUnitRepository.GetAsync(id);
            ou.ConcurrencyStamp = input.ConcurrencyStamp;
            ou.DisplayName = input.DisplayName;

            input.MapExtraPropertiesTo(ou);

            await _organizationUnitManager.UpdateAsync(ou);
            await CurrentUnitOfWork.SaveChangesAsync();

            return ObjectMapper.Map<OrganizationUnit, OrganizationUnitDto>(ou);
        }

        [Authorize(BookStore2Permissions.OrganizationUnits.ManageOU)]
        public async Task DeleteAsync(Guid id)
        {
            await _organizationUnitManager.DeleteAsync(id);
        }

        [Authorize(BookStore2Permissions.OrganizationUnits.ManageOU)]
        public async Task PutMoveAsync(Guid id, OrganizationUnitMoveInput input)
        {
            await _organizationUnitManager.MoveAsync(id, input.NewParentId);
        }

        [Authorize(BookStore2Permissions.OrganizationUnits.ManageMembers)]
        public async Task<PagedResultDto<IdentityUserDto>> GetMembersAsync(Guid id, GetIdentityUsersInput input)
        {
            var ou = await _organizationUnitRepository.GetAsync(id, includeDetails: false);
            var list = await _organizationUnitRepository.GetMembersAsync(ou, input.Sorting, input.MaxResultCount, input.SkipCount, input.Filter);
            var totalCount = await _organizationUnitRepository.GetMembersCountAsync(ou, input.Filter);

            return new PagedResultDto<IdentityUserDto>(
               totalCount,
               ObjectMapper.Map<List<IdentityUser>, List<IdentityUserDto>>(list)
               );
        }

        [Authorize(BookStore2Permissions.OrganizationUnits.ManageMembers)]
        public async Task UpdateMembersAsync(Guid id, OrganizationUnitUserInput input)
        {
            foreach (var userId in input.userIds)
            {
                await _identityUserManager.AddToOrganizationUnitAsync(userId, id);
            }
        }

        [Authorize(BookStore2Permissions.OrganizationUnits.ManageMembers)]
        public async Task DeleteMembersAsync(Guid id, Guid memberId)
        {
            await _identityUserManager.RemoveFromOrganizationUnitAsync(memberId, id);
        }

        [Authorize(BookStore2Permissions.OrganizationUnits.ManageRoles)]
        public async Task<PagedResultDto<IdentityRoleDto>> GetRolesAsync(Guid id, PagedAndSortedResultRequestDto input)
        {
            var ou = await _organizationUnitRepository.GetAsync(id, includeDetails: false);
            var list = await _organizationUnitRepository.GetRolesAsync(ou, input.Sorting, input.MaxResultCount, input.SkipCount);
            var totalCount = await _organizationUnitRepository.GetRolesCountAsync(ou);

            return new PagedResultDto<IdentityRoleDto>(
               totalCount,
               ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(list)
               );
        }

        [Authorize(BookStore2Permissions.OrganizationUnits.ManageRoles)]
        public async Task UpdateRolesAsync(Guid id, OrganizationUnitRoleInput input)
        {
            foreach (var roleId in input.roleIds)
            {
                await _organizationUnitManager.AddRoleToOrganizationUnitAsync(roleId, id);
            }
        }

        [Authorize(BookStore2Permissions.OrganizationUnits.ManageRoles)]
        public async Task DeleteRolesAsync(Guid id, Guid roleId)
        {
            await _organizationUnitManager.RemoveRoleFromOrganizationUnitAsync(roleId, id);
        }
    }
}
